package org.snowin.common;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.TTCCLayout;
import org.dom4j.*;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.openqa.selenium.WebDriver;
import org.snowin.InterfaceTestEngine;
import org.snowin.entities.InterfaceParam;
import org.snowin.entities.InterfaceTestEntity;
import org.snowin.ui.GenerateTheRunningData;
import org.snowin.utils.*;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URLClassLoader;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class Engine {
    private String caseDescription;
    String currentTestResult = "PASS";
    String currentTestCase;
    JavaCompilerTool javaCompilerTool;
    String sp = GlobalInfo.getFileSeparator();

    public Engine(HashMap<String, String> webDriverList, List<String> caseList, InitialEnvironment initialEnv) {

        new Engine(webDriverList, caseList, Main.initialEnv, null);
    }

    public Engine(HashMap<String, String> webDriverList, List<String> caseList, InitialEnvironment initialEnv, URLClassLoader urlClassLoader) {

        if (urlClassLoader != null) {
            javaCompilerTool = new JavaCompilerTool(urlClassLoader);
        } else {

            javaCompilerTool = new JavaCompilerTool(null);
        }

        combineAllPages();

        //配置log4j日志位置
        PropertyConfigurator.configure(GlobalInfo.getRootPath() + GlobalInfo.getFileSeparator() + "resources" + GlobalInfo.getFileSeparator() + "log4j.properties");

        Iterator iter = webDriverList.entrySet().iterator();
        GenerateTheRunningData generateTheRunningData = new GenerateTheRunningData();
        /*
        RunCaseBll runCase = null;
        if (GlobalInfo.isLaunchByUI) {
            runCase = new RunCaseBll();
        }
        */
        SimpleDateFormat sf = null;

        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            String browserName = (String) entry.getKey();
            String browserLocation = "";
            browserLocation = (String) entry.getValue();
            WebDriver webDriver = new InitWebDriver().initWebDrvier(browserName, browserLocation);
            GlobalInfo.currentWebDriver = webDriver;
            generateTheRunningData.initialDataFile(webDriver);
            Date date = new Date();
              sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//            ShortArrayGetAtMetaMethod arrayGetAtMetaMethod = new ShortArrayGetAtMetaMethod();
//            arrayGetAtMetaMethod.getDeclaringClass()


            String logFolder = GlobalInfo.getRootPath() + this.sp + "bin" + this.sp + "logs" + this.sp + sf.format(date).replace(":", "_") + "_" + webDriver.toString().split(":")[0];

            File testFolder = new File(logFolder);
            if (!testFolder.exists()) {
                testFolder.mkdir();
            }
            SessionData sessionData = initialEnv.getSessionData();
            GlobalInfo.HostName = sessionData.getHostName();

            webDriver.manage().timeouts().implicitlyWait(30L, TimeUnit.SECONDS);
            sessionData.setWebDriver(webDriver);
            for (String singleCase : caseList) {

                Logger logger = null;
                try {
                    String[] singleCaseArry = singleCase.split(":");
                    if (singleCaseArry[2].equalsIgnoreCase("common")) {
                        String className = singleCaseArry[0] + ".class";
                        logger = Logger.getLogger(className);
                    }
//                     将来 提供接口 调用
                    else if (singleCaseArry[2].equalsIgnoreCase("interface")) {
                        logger = Logger.getRootLogger();
                    }
                    logger.info("###########################################################################################");
                    logger.info("##      INVOICE                                                               INVOICE    ##");
                    logger.info("##             INVOICE                                                 INVOICE           ##");
                    logger.info("##                    INVOICE                                   INVOICE                  ##");
                    logger.info("##                           INVOICE                     INVOICE                         ##");
                    logger.info("##                                  INVOICE       INVOICE                                ##");
                    logger.info("##                                         INVOICE                                       ##");
                    logger.info("###################################### WTD Start ##########################################");
                    logger.info("################## Start to Execute Case: 【 " + singleCase.replace(":", "---") + " 】####################");

                    this.currentTestCase = (singleCase.replace(":", "~") + "~" + logFolder + this.sp + singleCaseArry[2] + "_" + singleCaseArry[1] + "_" + singleCaseArry[0]);
                    /*
                    if (GlobalInfo.isLaunchByUI)
                    {
                        runCase.setCase(this.currentTestCase);
                        runCase.updateCurrentCase();
                    }
                    */
                    if (singleCaseArry[2].equalsIgnoreCase("common")) {
                        String className = singleCaseArry[0] + ".class";

                        runCommonCases(singleCase, sessionData, logger, logFolder);
                    } else if (singleCaseArry[2].equalsIgnoreCase("interface")) {
//                        String ilogFolder = GlobalInfo.getRootPath() + this.sp + "bin" + this.sp + "logs" + this.sp + sf.format(date).replace(":", "_") + "_" + "WebInterface".trim();
//                        File interfaceLogFolder = new File(ilogFolder);
//                        if(!interfaceLogFolder.exists()){
//                            interfaceLogFolder.mkdir();
//                        }
                        runInterfaceCases(singleCase, sessionData, logger, logFolder);
                    }
                    logger.info("################## Finish to Execute Case: 【 " + singleCase.replace(":", "---") + " 】####################");
                    logger.info("#################### Test Result is : 【 " + this.currentTestResult + " 】####################");

                    logger.info("################## The Test Description is : 【 " + this.caseDescription + " 】####################");

                    logger.info("######################################  WTD Finished ######################################");
                    logger.info("###########################################################################################");
                    logger.info("###########################################################################################");
                    logger.info("###########################################################################################");
                } catch (Exception e) {
                    logger.info("########### [ Error ]: Run Case Faild ,Exception Ocurrs !!!!!!###########");
                    logger.info("Exception", e);
                }
                /*
                if (GlobalInfo.isLaunchByUI) {
                    generateTheRunningData.createData(this.currentTestCase, this.currentTestResult);

                    runCase.setResult(this.currentTestResult);
                    runCase.updateCompleteUI();
                }
*/
            }
            tearDown(logFolder, webDriver, initialEnv);
        }
        clearEnv();
    }

    private void clearEnv() {
        try {
            Thread.sleep(3000L);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

    }

    private void tearDown(String logFolder, WebDriver webDrvier, InitialEnvironment initialEnv) {

        try {
            webDrvier.close();
            webDrvier.quit();
        } catch (Exception localException) {
        }
        mergeAllResult(logFolder);


        sendReport(logFolder + "/allResult.xml", initialEnv.getMailReceptors(), initialEnv.getProxy(), initialEnv.getMailSubject(), initialEnv.getMailSender());
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void sendReport(String allResultXml, List<String> receptions, String proxy, String mailSubject, HashMap<String, String> mailSender) {

        MailBuilder builder = new MailBuilder();
        builder.setReceptions(receptions);
        builder.setProxy(proxy);
        builder.setMailSubject(mailSubject);
        builder.setMailSender(mailSender);
        try
        {
            InputStreamReader isr = new InputStreamReader(new FileInputStream(allResultXml), "UTF-8");


            builder.setContent(isr, "conf//report.xsl");
            builder.doSendMail();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }


    private void mergeAllResult(String logFolder) {

        String fileName = logFolder + System.getProperty("file.separator") + "allResult.xml" ;
        File allResultFile  = new File(fileName);
        if (!allResultFile.exists()){
            try {
                allResultFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            Document document = DocumentHelper.createDocument();

            Element root = document.addElement("TestResult");
            List<File> listFiles = new ArrayList();
            List<File> lists = search(logFolder, listFiles);
            for (File f : lists) {
                SAXReader reader = new SAXReader();
                Document tmpDoc = reader.read(f);
                List<Element> elements = tmpDoc.selectNodes("//TestResult");
                for (Element e : elements) {
                    root.appendContent((Branch) e.clone());
                }
            }
            OutputFormat outFmt = new OutputFormat("\t", true, "UTF-8");

            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(fileName));

            xmlWriter.write(document);
            xmlWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<File> search(String folder, List<File> listFiles) {

        File[] files = new File(folder).listFiles();
        if (files == null) {
            return null;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                search(file.toString(), listFiles);
            } else if (file.getName().toLowerCase().equalsIgnoreCase("result.xml")) {
                listFiles.add(file);
            }
        }
        return listFiles;
    }

    private void runCommonCases(String caseName, SessionData sessionData, Logger logger, String logFolder) {
        String[] singleCase = caseName.split(":");
        String caseJaveFile = GlobalInfo.getRootPath() + this.sp + "wtdwebuicases" + this.sp + singleCase[1] + this.sp + singleCase[0] + ".java";

        boolean status = this.javaCompilerTool.comiple(GlobalInfo.getRootPath() + this.sp + "bin" + this.sp + "webuicaseclasses", caseJaveFile);

        Class cl = null;

        try {
            if (status) {
                try {
                    cl = JavaCompilerTool.getClassLoader().loadClass(singleCase[1] + "." + singleCase[0]);
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }

            sessionData.setLogger(logger);
            sessionData.setCaseName(singleCase[0]);
            String caseLogFolder = logFolder + this.sp + singleCase[2] + "_" + singleCase[1] + "_" + singleCase[0];
            File caseFolder = new File(caseLogFolder);
            if (!caseFolder.exists()) {
                caseFolder.mkdir();
            }
            sessionData.setLogFolder(caseLogFolder);
            BaseCase testObj = null;
            if (cl != null) {
//                Constructor strCtor = cl.getConstructor(new Class[0]);
                Constructor strCtor = cl.getConstructor();


                testObj = (BaseCase) strCtor.newInstance(new Object[0]);
                testObj.initialSessionData(sessionData);
                FileAppender appender = null;
                try {
                    appender = new FileAppender(new TTCCLayout(), caseLogFolder + this.sp + singleCase[0] + ".log", false);
                } catch (IOException e) {
                    e.printStackTrace();
                }


                logger.addAppender(appender);

                generateResultXml(caseFolder.toString(), logger, caseName);
                try {
                    if (testObj != null) {
                        testObj.execute();


                    } else {
                        testObj = new VirtualBaseCase();
                        testObj.setTestResult("block");
                        testObj.setErrorInfo("编译case出错，请检查case的代码的语法错误,case 路径：" + caseJaveFile);
                        logger.error("编译case出错，请检查case的代码的语法错误，case 路径：" + caseJaveFile);
                    }
                    try {
                        if (testObj != null) {
                            new ScreenCapture(sessionData);
                            this.caseDescription = testObj.getDescription();
                            this.currentTestResult = testObj.getTestResult();
                        } else {
                            this.caseDescription = "";
                            this.currentTestResult = "";
                        }
                    } catch (Exception localException1) {
                    }
                    setCommonTestCaseResult(caseFolder.toString(), sessionData, singleCase, testObj);
                } catch (Exception e) {
                    testObj.setTestResult("block");


                    testObj.setErrorInfo(e.getMessage());
                    logger.info("errorStack", e);
                } finally {
                    try {
                        if (testObj != null) {
                            new ScreenCapture(sessionData);
                            this.caseDescription = testObj.getDescription();
                            this.currentTestResult = testObj.getTestResult();
                        } else {
                            this.caseDescription = "";
                            this.currentTestResult = "";
                        }
                    } catch (Exception localException3) {
                    }
                }

            }

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    private void setCommonTestCaseResult(String parentFolder, SessionData sessionData, String[] caseName, BaseCase bs) {
        String resultFile = parentFolder + System.getProperty("file.separator") + "result.xml";

        String singleTestFolder = parentFolder.replace(this.sp + caseName[2] + "_" + caseName[1] + "_" + caseName[0], "");
        String[] temArray = singleTestFolder.split("\\\\");

        singleTestFolder = temArray[(temArray.length - 1)];
        try
        {
            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(resultFile));
            Element root = document.getRootElement();
            Element element = (Element)document.selectSingleNode("//TestCase[@name='" + caseName[0] + "' and @caseType='" + caseName[2] + "' and @caseModule='" + caseName[1] + "']");



            List<Element> testPointElements = element.selectNodes("TestPoint");

            List<Element> testPointPASS = element.selectNodes("TestPoint[@result='pass']");
            if (bs.getTestResult().equalsIgnoreCase("block"))
            {
                element.setAttributeValue("result", "block");
                bs.setTestResult("block");
            }
            else
            {
                if (testPointElements.size() != testPointPASS.size())
                {
                    element.setAttributeValue("result", "fail");
                    bs.setTestResult("fail");
                }
                else
                {
                    element.setAttributeValue("result", "pass");
                }
                element.setAttributeValue("description", bs.getDescription());
            }
            element.setAttributeValue("errorInfo", bs.getErrorInfo());

            element.setAttributeValue("logUrl", this.sp + this.sp + GlobalInfo.pcIP + this.sp + GlobalInfo.pcHostName + this.sp + singleTestFolder + this.sp + caseName[2] + "_" + caseName[1] + "_" + caseName[0]);
            element.setAttributeValue("webBrowser", sessionData.getWebDriver().toString().split(":")[0]);

            this.currentTestResult = bs.getTestResult();
            OutputFormat outFmt = new OutputFormat("\t", true, "UTF-8");
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(resultFile));

            xmlWriter.write(document);
            xmlWriter.close();
        }
        catch (Exception e)
        {
            sessionData.getLogger().error(" ****** Modify Result XML Failed ******** ");
            sessionData.getLogger().error("errorInfo", e);
        }

    }

    private String generateResultXml(String parentFolder, Logger logger, String caseName) {

            String[] singelCase = caseName.split(":");
            String fileName = parentFolder + System.getProperty("file.separator") + "result.xml";
            try
            {
                Document document = DocumentHelper.createDocument();

                Element root = document.addElement("TestResult");
                Element testCase = root.addElement("TestCase");
                testCase.addAttribute("name", singelCase[0]);
                testCase.addAttribute("caseModule", singelCase[1]);
                testCase.addAttribute("caseType", singelCase[2]);
                testCase.addAttribute("description", "");
                testCase.addAttribute("result", "");
                testCase.addAttribute("errorInfo", "");

                OutputFormat outFmt = new OutputFormat("\t", true, "UTF-8");

                XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(fileName));

                xmlWriter.write(document);
                xmlWriter.close();
            }
            catch (Exception e)
            {
                logger.error(" ****** Create Result XML Failed ******** ");
                logger.error("errorInfo", e);
            }
            return fileName;



    }


    private void runInterfaceCases(String caseName, SessionData sessionData, Logger logger, String logFolder) throws IOException {
        String[] singleCase = caseName.split(":");
        try
        {
            sessionData.setLogger(logger);

            sessionData.setCaseName(singleCase[0]);

            String caseLogFolder = logFolder + this.sp + singleCase[1] + "_" + singleCase[0];

            File caseFolder = new File(caseLogFolder);
            if (!caseFolder.exists()) {
                caseFolder.mkdir();
            }
            sessionData.setLogFolder(caseLogFolder);

            SAXReader saxReader = new SAXReader();
            String sp = System.getProperty("file.separator");
            String caseFolderString = GlobalInfo.getRootPath() + sp + "wtdapicases" + sp + "interfaces";
            String[] caseArray = caseName.split(":");
            Document doc = saxReader.read(new File(caseFolderString + sp + caseArray[1] + sp + caseArray[0] + ".xml"));

            Element caseElem = (Element)doc.selectSingleNode("//case");
            String caseDescription = caseElem.attributeValue("description");
            String contentType = caseElem.attributeValue("contenttype");
            if (null == contentType) {
                contentType = "";
            }
            String sendType = caseElem.attributeValue("type");

            List<Element> globalVaribles = caseElem.selectNodes("//setGlobalVarible");


            Element caseApiElement = (Element)caseElem.selectSingleNode("testApi");
            String testApi = caseApiElement.attributeValue("api");
            String realApiUrl = "";
            String localUrl = sessionData.getHostName();


            realApiUrl = testApi;


            List<Element> tests = caseElem.selectNodes("test");
            List<InterfaceTestEntity> interfaceTests = new ArrayList();
            for (Element e : tests)
            {
                List<Element> paramElems = e.selectNodes("parameter");

                InterfaceTestEntity interfaceTestEntity = new InterfaceTestEntity();
                List<Element> bodyElems = e.selectNodes("body");
                String body;
                if (bodyElems.size() > 0)
                {
                    Element el = (Element)bodyElems.get(0);
                    body = el.attributeValue("value");
                    interfaceTestEntity.setBody(body);
                }
                List<InterfaceParam> parrams = new ArrayList();
                for (Element el : paramElems)
                {
                    InterfaceParam paramEntity = new InterfaceParam();
                    String isRandom = "false";
                    if (null != el.attributeValue("isRandom")) {
                        isRandom = el.attributeValue("isRandom");
                    }
                    paramEntity.setName(el.attributeValue("name"));
                    paramEntity.setRandom(isRandom.equalsIgnoreCase("true"));
                    paramEntity.setValue(el.attributeValue("value"));
                    parrams.add(paramEntity);
                }
                interfaceTestEntity.setExpectContansString(e.attributeValue("expectContansString"));
                interfaceTestEntity.setInterfaceParams(parrams);
                interfaceTestEntity.setName(e.attributeValue("name"));
                interfaceTestEntity.setPreExcution(e.attributeValue("preExcution"));
                interfaceTestEntity.setContentType(contentType);
                interfaceTests.add(interfaceTestEntity);
            }
            FileAppender appender = new FileAppender(new TTCCLayout(), caseLogFolder + sp + singleCase[0] + ".log", false);

            logger.addAppender(appender);
            generateResultXml(caseFolder.toString(), logger, caseName);



            InterfaceTestEngine interfaceTestEngine = new InterfaceTestEngine(realApiUrl, interfaceTests, sendType, logger, globalVaribles);
            setInterfaceTestCaseResult(caseFolder.toString(), sessionData, singleCase, interfaceTestEngine.getTestResult());
        }
        catch (DocumentException e)
        {
            e.printStackTrace();
        }


    }

    private void setInterfaceTestCaseResult(String parentFolder, SessionData sessionData, String[] caseName, List<List<String>> testResult) {
        String resultFile = parentFolder + System.getProperty("file.separator") + "result.xml";

        String singleTestFolder = parentFolder.replace(this.sp + caseName[2] + "_" + caseName[1] + "_" + caseName[0], "");
        String[] temArray = singleTestFolder.split("\\\\");

        singleTestFolder = temArray[(temArray.length - 1)];
        try
        {
            SAXReader reader = new SAXReader();
            Document document = reader.read(new File(resultFile));
            Element root = document.getRootElement();

            Element element = (Element)document.selectSingleNode("//TestCase[@name='" + caseName[0] + "' and @caseType='" + caseName[2] + "' and @caseModule='" + caseName[1] + "']");

            int i = 1;
            this.currentTestResult = "pass";
            for (List<String> caseResult : testResult)
            {
                Element temEl = element.addElement("TestPoint");
                temEl.addAttribute("name", "Test " + i);
                temEl.addAttribute("caseType", "interface");
                temEl.addAttribute("result", (String)caseResult.get(2));
                temEl.addAttribute("expectString", (String)caseResult.get(3));
                temEl.addAttribute("response", (String)caseResult.get(1));
                temEl.addAttribute("description", "");
                temEl.addAttribute("testContent", (String)caseResult.get(0));
                if (!((String)caseResult.get(2)).contains("pass"))
                {
                    temEl.addAttribute("errorInfo", (String)caseResult.get(1));
                    this.currentTestResult = "fail";
                }
                else
                {
                    temEl.addAttribute("errorInfo", "");
                }
                i++;
            }
            element.setAttributeValue("errorInfo", "");
            element.setAttributeValue("result", this.currentTestResult);
            element.setAttributeValue("logUrl", this.sp + this.sp + GlobalInfo.pcIP + this.sp + GlobalInfo.pcHostName + this.sp + singleTestFolder + this.sp + caseName[2] + "_" + caseName[1] + "_" + caseName[0]);
            element.setAttributeValue("webBrowser", sessionData.getWebDriver().toString().split(":")[0]);
            OutputFormat outFmt = new OutputFormat("\t", true, "UTF-8");
            XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(resultFile));

            xmlWriter.write(document);
            xmlWriter.close();
        }
        catch (Exception e)
        {
            sessionData.getLogger().error(" ****** Modify Result XML Failed ******** ");
            sessionData.getLogger().error("errorInfo", e);
        }

    }

    private void combineAllPages() {
        String sp = GlobalInfo.getFileSeparator();
        File pagesFolder = new File(GlobalInfo.getRootPath() + sp + "wtdwebuicases" + sp + "pages");

        File[] files = pagesFolder.listFiles();
        for (File file : files) {
            if (file.getName().endsWith(".java")) {
                this.javaCompilerTool.comiple(GlobalInfo.getRootPath() + sp + "bin" + sp + "webuicaseclasses", file.getAbsolutePath());
            }
        }

    }
}
